{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "eager.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CCQY7jpBfMur"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "z6X9omPnfO_h",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2QQJJyDzqGRb"
      },
      "source": [
        "# Eager execution\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "B1xdylywqUSX"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/r1/guide/eager.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запусти в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/r1/guide/eager.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучай код на GitHub</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FHHSnZ-0bIaM",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "EGjDcGxIqEfX"
      },
      "source": [
        "\n",
        "\n",
        "Eager execution в TensorFlow - это интерактивный режим, в котором все вычисления выполняются мгновенно: операции возвращают конкретные значения, вместо построения вычислительных графов. Это позволяет легко запускать любой код в TensorFlow, упрощает отладку моделей и устраняет необходимость в шаблонном boilerplate коде. Пройди все этапы в этом руководстве, просто запуская примеры кода в интерпретаторе `Python` ниже.\n",
        "\n",
        "Eager execution является гибкой платформой машинного обучения для проведения\n",
        "исследований и экспериментов. Ее основные преимущества:\n",
        "\n",
        "* *Интуитивный интерфейс* — структурируй свой код и используй стандартные структуры\n",
        "данных Python. Быстро проверяй гипотезы с помощью небольших моделей на малых данных\n",
        "* *Легкая отладка кода* — Производи любые операции непосредственно на готовых\n",
        "моделях и проверяй изменения. Используй стандартные инструменты для отладки\n",
        "Python кода для незамедлительного отчета об ошибках\n",
        "* *Естественный порядок выполнения* — Используй порядок выполнения Python вместо графа вычислений, упрощая спецификацию динамических моделей\n",
        "\n",
        "Eager execution поддерживает большинство операций TensoFlow и ускорение при помощи GPU.\n",
        "Смотри основные примеры, которые можно\n",
        "запускать в eager execution здесь:\n",
        "[Примеры в Eager Execution](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples).\n",
        "\n",
        "Обрати внимание: некоторые модели могут испытывать повышенную\n",
        "нагрузку при активированном eager execution. Улучшения производительности этого режима находятся в непрерывной разработке, пожалуйста\n",
        "[сообщайте о багах](https://github.com/tensorflow/tensorflow/issues) если\n",
        "сталкиваетесь с какими-либо проблемами."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RBAeIwOMrYk8"
      },
      "source": [
        "## Установка и использование\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "48P3-8q4qEfe"
      },
      "source": [
        "Чтобы запустить eager execution, добавь `tf.enable_eager_execution()` в начало\n",
        "программы или консольной сессии. Не добавляй эту операцию к другим модулям,\n",
        "которые вызывает программа."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "7aFsD8csqEff",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals, unicode_literals\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "tf.enable_eager_execution()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "x_G1zZT5qEfh"
      },
      "source": [
        "Теперь ты можешь запускать любые операции TensorFlow и получать результаты мгновенно:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5hien2IEqwLQ",
        "colab": {}
      },
      "source": [
        "tf.executing_eagerly()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "9gsI54pbqEfj",
        "colab": {}
      },
      "source": [
        "x = [[2.]]\n",
        "m = tf.matmul(x, x)\n",
        "print(\"привет, {}\".format(m))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ajFn6qsdqEfl"
      },
      "source": [
        "Включение режима eager execution изменяет то, как себя ведут операции TensorFlow:\n",
        "теперь они выполняются мгновенно. Объекты `tf.Tensor` относятся к конкретным\n",
        "значениям, вместо символических имен вычислительных графов. Так как теперь\n",
        "нет вычислительного графа, который нужно построить и затем запустить в сессии,\n",
        "можно легко инспектировать результаты при помощи функции `print()`\n",
        "или отладчика. Вычисления, вывод данных и проверка значений тензоров\n",
        "не нарушает порядок выполнения для расчета градиентов.\n",
        "\n",
        "Eager execution легко работает с [NumPy](http://www.numpy.org/). Операции NumPy\n",
        "принимают аргументы `tf.Tensor`. [Математические операции](https://www.tensorflow.org/api_guides/python/math_ops) TensorFlow конвертируют\n",
        "объекты Python и массивы NumPy в объекты `tf.Tensor`. Метод\n",
        "`tf.Tensor.numpy` возвращает значение объекта как массив NumPy `ndarray`."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sTO0_5TYqz1n",
        "colab": {}
      },
      "source": [
        "a = tf.constant([[1, 2],\n",
        "                 [3, 4]])\n",
        "print(a)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Dp14YT8Gq4r1",
        "colab": {}
      },
      "source": [
        "# Проверяем при помощи функции `print`\n",
        "b = tf.add(a, 1)\n",
        "print(b)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "69p3waMfq8cQ",
        "colab": {}
      },
      "source": [
        "# Поддерживается перегрузка операторов\n",
        "print(a * b)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "Ui025t1qqEfm",
        "colab": {}
      },
      "source": [
        "# Используем значения NumPy\n",
        "import numpy as np\n",
        "\n",
        "c = np.multiply(a, b)\n",
        "print(c)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Tq_aFRzWrCua",
        "colab": {}
      },
      "source": [
        "# Получаем значение NumPy из тензора\n",
        "print(a.numpy())\n",
        "# => [[1 2]\n",
        "#     [3 4]]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Jdg3nZFwqEfp"
      },
      "source": [
        "Модуль `tf.contrib.eager` содержит символы, доступные как в eager, так и в стандартном режиме graph execution, и является весьма полезным при [работе с графами](#work_with_graphs):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "N-2lSGiHqEfq",
        "colab": {}
      },
      "source": [
        "tfe = tf.contrib.eager"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "H08f9ss9qEft"
      },
      "source": [
        "## Динамический порядок выполнения\n",
        "\n",
        "Большим преимуществом eager execution является то, что весь\n",
        "функционал языка хоста доступен в то время, как запущена модель.\n",
        "Например, можно легко написать решение задачи [fizzbuzz](https://en.wikipedia.org/wiki/Fizz_buzz):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "0fudRMeUqEfu",
        "colab": {}
      },
      "source": [
        "def fizzbuzz(max_num):\n",
        "  counter = tf.constant(0)\n",
        "  max_num = tf.convert_to_tensor(max_num)\n",
        "  for num in range(1, max_num.numpy()+1):\n",
        "    num = tf.constant(num)\n",
        "    if int(num % 3) == 0 and int(num % 5) == 0:\n",
        "      print('FizzBuzz')\n",
        "    elif int(num % 3) == 0:\n",
        "      print('Fizz')\n",
        "    elif int(num % 5) == 0:\n",
        "      print('Buzz')\n",
        "    else:\n",
        "      print(num.numpy())\n",
        "    counter += 1"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "P2cKknQWrJLB",
        "colab": {}
      },
      "source": [
        "fizzbuzz(15)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7kA-aC3BqEfy"
      },
      "source": [
        "Есть условные операторы, которые зависят от значений тензоров, и эти\n",
        "значения выводятся в среде выполнения нашей программы."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zn3mp-j6rjnk"
      },
      "source": [
        "## Построение модели\n",
        "\n",
        "Многие модели машинного обучения состоят из сочетания слоев. Когда\n",
        "мы используем TensorFlow с eager execution, ты можешь либо создавать\n",
        "свои собственные слои, или использовать уже готовые, которые\n",
        "определены в `tf.keras.layers`.\n",
        "\n",
        "В то время как ты можешь использовать любой объект Python для\n",
        "представления слоя, в TensorFlow есть удобный способ использования\n",
        "базовых классов слоев из `tf.keras.layers.Layers`. Используй их\n",
        "для создания своего собственного слоя:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "kvXiJsmyqEfz",
        "colab": {}
      },
      "source": [
        "class MySimpleLayer(tf.keras.layers.Layer):\n",
        "  def __init__(self, output_units):\n",
        "    super(MySimpleLayer, self).__init__()\n",
        "    self.output_units = output_units\n",
        "\n",
        "  def build(self, input_shape):\n",
        "    # Метод `build` вызывается первый раз, когда используется слой.\n",
        "    # Создание переменных в build() позволяет задать размерность тензоров в зависимости\n",
        "    # от размерности входных параметров, и таким образом устранить необходимость\n",
        "    # пользователю уточнять формы полностью. Также возможно создавать переменные\n",
        "    # во время __init__(), если ты уже знаешь их полные формы.\n",
        "    self.kernel = self.add_variable(\n",
        "      \"kernel\", [input_shape[-1], self.output_units])\n",
        "\n",
        "  def call(self, input):\n",
        "    # Перепишем call() вместо __call__, чтобы мы могли вести счет.\n",
        "    return tf.matmul(input, self.kernel)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "eo3qgrVCqEf2"
      },
      "source": [
        "Используй слой `tf.keras.layers.Dense` вместо `MySimpleLayer` выше, так как\n",
        "он включает в себя надмножество (также может включать в себя смещение `bias`).\n",
        "\n",
        "Когда составляешь слои модели, ты можешь использовать `tf.keras.Sequential` для\n",
        "создания моделей, которые являются линейный стеком слоев. Это легко использовать\n",
        "для стандартных моделей:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "VrfLnhNPqEf3",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Dense(10, input_shape=(784,)),  # укажем размерность входных данных\n",
        "  tf.keras.layers.Dense(10)\n",
        "])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Dms3mduTqEf6"
      },
      "source": [
        "Альтернативный способ - организовать модели в классы из `tf.keras.Model`.\n",
        "Это контейнер слоев, который также сам является  слоем, что позволяет\n",
        "объектам `tf.keras.Model` содержать в себе другие объекты `tf.keras.Model`."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "MwWxQmNOqEf7",
        "colab": {}
      },
      "source": [
        "class MNISTModel(tf.keras.Model):\n",
        "  def __init__(self):\n",
        "    super(MNISTModel, self).__init__()\n",
        "    self.dense1 = tf.keras.layers.Dense(units=10)\n",
        "    self.dense2 = tf.keras.layers.Dense(units=10)\n",
        "\n",
        "  def call(self, input):\n",
        "    \"\"\"Запускаем модель.\"\"\"\n",
        "    result = self.dense1(input)\n",
        "    result = self.dense2(result)\n",
        "    result = self.dense2(result)  # повторно используем переменные из слоя dense2\n",
        "    return result\n",
        "\n",
        "model = MNISTModel()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "a639YaF4qEf-"
      },
      "source": [
        "Необязательно устанавливать размерность входных данных для классов `tf.keras.Model`,\n",
        "поскольку параметры устанавливаются первый раз и передаются слою.\n",
        "\n",
        "Классы `tf.keras.layers` создают и содержат собственные переменные\n",
        "моделей, время действия которых привязаны к объектам слоев. Чтобы разделить\n",
        "переменные слоев, необходимо разделить их объекты."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "8huKpuuAwICq"
      },
      "source": [
        "## Обучение в Eager"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mp2lCCZYrxHd"
      },
      "source": [
        "### Вычисление градиентов\n",
        "\n",
        "[Автоматическое дифференцирование](https://en.wikipedia.org/wiki/Automatic_differentiation)\n",
        "полезна для реализации алгоритмов машинного обучения, например таких ка\n",
        "[метод обратного распространения ошибки backpropagation](https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%BE%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE_%D1%80%D0%B0%D1%81%D0%BF%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F_%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B8)\n",
        "для обучения нейронных сетей. Во время eager execution используй `tf.GradientTape`\n",
        "для записи операций и для последующего вычисления градиента.\n",
        "\n",
        "`tf.GradientTape` - это встроенная возможность обеспечения максимальной производительности модели, когда не записываются операции. Поскольку\n",
        "могут возникать разные операции во время каждого вызова, все\n",
        "операции прямого прохода записываются на \"пленку\" (\"tape\"). Для\n",
        "вычисления градиента, пленка воспроизводится в обратном\n",
        "порядке, а затем сбрасывается. Конкретная запись `tf.GradientTape`\n",
        "может произвести расчет только одного градиента; все последующие\n",
        "вызовы выдадут ошибку рабочей среды."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "7g1yWiSXqEf-",
        "colab": {}
      },
      "source": [
        "w = tf.Variable([[1.0]])\n",
        "with tf.GradientTape() as tape:\n",
        "  loss = w * w\n",
        "\n",
        "grad = tape.gradient(loss, w)\n",
        "print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vkHs32GqweYS"
      },
      "source": [
        "### Обучение модели\n",
        "\n",
        "В следующем примере мы создадим многослойную модель, которая будет\n",
        "классифицировать стандартный датасет MNIST, состоящий из изображений\n",
        "рукописных чисел. Данный пример продемонстрирует API оптимизатора\n",
        "и слоя для создания обучаемых графов в среде eager execution."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "38kymXZowhhz",
        "colab": {}
      },
      "source": [
        "# Загружаем и форматируем данные mnist\n",
        "(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()\n",
        "\n",
        "dataset = tf.data.Dataset.from_tensor_slices(\n",
        "  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),\n",
        "   tf.cast(mnist_labels,tf.int64)))\n",
        "dataset = dataset.shuffle(1000).batch(32)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rl1K8rOowmwT",
        "colab": {}
      },
      "source": [
        "# Создаем модель\n",
        "mnist_model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu', input_shape=(None,None,1)),\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),\n",
        "  tf.keras.layers.GlobalAveragePooling2D(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "fvyk-HgGwxwl"
      },
      "source": [
        "Еще не приступив к обучению, попробуй вызвать модель и проинспектировать\n",
        "вывод данных в eager execution:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "BsxystjBwxLS",
        "colab": {}
      },
      "source": [
        "for images,labels in dataset.take(1):\n",
        "  print(\"Логиты: \", mnist_model(images[0:1]).numpy())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Y3PGa8G7qEgB"
      },
      "source": [
        "В то время как модели Keras имеют встроенный тренировочный цикл (используется в методе `fit`), иногда\n",
        "требуется более тонкая настройка. Вот пример, когда тренировочный цикл реализован в eager:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "bzRhM7JDnaEG",
        "colab": {}
      },
      "source": [
        "optimizer = tf.train.AdamOptimizer()\n",
        "\n",
        "loss_history = []"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "0m1xAXrmqEgJ",
        "colab": {}
      },
      "source": [
        "for (batch, (images, labels)) in enumerate(dataset.take(400)):\n",
        "  if batch % 80 == 0:\n",
        "    print()\n",
        "  print('.', end='')\n",
        "  with tf.GradientTape() as tape:\n",
        "    logits = mnist_model(images, training=True)\n",
        "    loss_value = tf.losses.sparse_softmax_cross_entropy(labels, logits)\n",
        "\n",
        "  loss_history.append(loss_value.numpy())\n",
        "  grads = tape.gradient(loss_value, mnist_model.trainable_variables)\n",
        "  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables),\n",
        "                            global_step=tf.train.get_or_create_global_step())"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5vG5ql_2vYB5",
        "colab": {}
      },
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.plot(loss_history)\n",
        "plt.xlabel('Батч #')\n",
        "plt.ylabel('Потери [энтропия]')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9ODzXW8xqEgU"
      },
      "source": [
        "В этом примере используется\n",
        "[модуль dataset.py](https://github.com/tensorflow/models/blob/master/official/mnist/dataset.py)\n",
        "из\n",
        "[примера TensorFlow MNIST](https://github.com/tensorflow/models/tree/master/official/mnist);\n",
        "загрузи этот файл в папку на своем устройстве. Затем запусти код для загрузки\n",
        "данных MNIST в рабочую папку и подготовь `tf.data.Dataset`\n",
        "к обучению:"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kKpOlHPLqEgl"
      },
      "source": [
        "### Переменные и оптимизаторы\n",
        "\n",
        "Объекты `tf.Variable` хранят переменные `tf.Tensor` значения, доступ\n",
        "к которым предоставляется во время обучения, чтобы проще производить\n",
        "автоматическую дифференцирование. Параметры модели могут быть\n",
        "сохранены в классах как переменные.\n",
        "\n",
        "Лучше сохранять параметры модели, используя `tf.Variable` при помощи\n",
        "`tf.GradientTape`. Например таким образом, автоматическая дифференцирование\n",
        "примера выше может быть перезаписана:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "nnQLBYmEqEgm",
        "colab": {}
      },
      "source": [
        "class Model(tf.keras.Model):\n",
        "  def __init__(self):\n",
        "    super(Model, self).__init__()\n",
        "    self.W = tf.Variable(5., name='weight')\n",
        "    self.B = tf.Variable(10., name='bias')\n",
        "  def call(self, inputs):\n",
        "    return inputs * self.W + self.B\n",
        "\n",
        "# Маленький датасет из точек около 3 * x + 2\n",
        "NUM_EXAMPLES = 2000\n",
        "training_inputs = tf.random_normal([NUM_EXAMPLES])\n",
        "noise = tf.random_normal([NUM_EXAMPLES])\n",
        "training_outputs = training_inputs * 3 + 2 + noise\n",
        "\n",
        "# Функция потерь для оптимизации\n",
        "def loss(model, inputs, targets):\n",
        "  error = model(inputs) - targets\n",
        "  return tf.reduce_mean(tf.square(error))\n",
        "\n",
        "def grad(model, inputs, targets):\n",
        "  with tf.GradientTape() as tape:\n",
        "    loss_value = loss(model, inputs, targets)\n",
        "  return tape.gradient(loss_value, [model.W, model.B])\n",
        "\n",
        "# Определим:\n",
        "# 1. Модель\n",
        "# 2. Производные функции потерь с учетом параметров модели\n",
        "# 3. Стратегию обновления переменных, основываясь на производных\n",
        "model = Model()\n",
        "optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)\n",
        "\n",
        "print(\"Изначальная потеря: {:.3f}\".format(loss(model, training_inputs, training_outputs)))\n",
        "\n",
        "# Тренировочный цикл:\n",
        "for i in range(300):\n",
        "  grads = grad(model, training_inputs, training_outputs)\n",
        "  optimizer.apply_gradients(zip(grads, [model.W, model.B]),\n",
        "                            global_step=tf.train.get_or_create_global_step())\n",
        "  if i % 20 == 0:\n",
        "    print(\"Потеря на шаге {:03d}: {:.3f}\".format(i, loss(model, training_inputs, training_outputs)))\n",
        "\n",
        "print(\"Окончательная потеря: {:.3f}\".format(loss(model, training_inputs, training_outputs)))\n",
        "print(\"W = {}, B = {}\".format(model.W.numpy(), model.B.numpy()))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rPjb8nRWqEgr"
      },
      "source": [
        "## Использование объектов для состояния в eager execution\n",
        "\n",
        "В graph execution состояния программы (такие, как переменные) хранятся\n",
        "в глобальных коллекциях и их срок действия определяется объектом\n",
        "`tf.Session`. Для сравнения, во время eager execution срок действия\n",
        "состояний объектов определяется сроком действия соответствующего\n",
        "объекта Python.\n",
        "\n",
        "### Переменные объекты\n",
        "\n",
        "Во время eager execution, переменные сохраняются и хранятся до тех пор, пока не\n",
        "будет убрана последняя отсылка к объекту, и только тогда он будет удален."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "A2boS674qEgs",
        "colab": {}
      },
      "source": [
        "if tf.test.is_gpu_available():\n",
        "  with tf.device(\"gpu:0\"):\n",
        "    v = tf.Variable(tf.random_normal([1000, 1000]))\n",
        "    v = None  # v больше не занимает место в памяти GPU"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "scMjg6L6qEgv"
      },
      "source": [
        "### Объектное сохранение\n",
        "\n",
        "`tf.train.Checkpoint` может сохранять и загружать `tf.Variable`s как *в*,\n",
        "так и *из* контрольных точек:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7z5xRfdHzZOQ",
        "colab": {}
      },
      "source": [
        "x = tf.Variable(10.)\n",
        "checkpoint = tf.train.Checkpoint(x=x)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IffrUVG7zyVb",
        "colab": {}
      },
      "source": [
        "x.assign(2.)   # Назначим новое значение переменной и сохраним\n",
        "checkpoint_path = './ckpt/'\n",
        "checkpoint.save('./ckpt/')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "eMT9koCoqEgw",
        "colab": {}
      },
      "source": [
        "x.assign(11.)  # Изменим переменную после сохранения\n",
        "\n",
        "# Восстановим значение из контрольной точки\n",
        "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))\n",
        "\n",
        "print(x)  # => 2.0"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vbFnP-yLqEgx"
      },
      "source": [
        "Для сохранения и загрузки моделей `tf.train.Checkpoint` сохраняет внутреннее\n",
        "состояние объектов без требования скрытых переменных. Чтобы сохранить\n",
        "текущее состояние `модели`, выбранный `оптимизатор` и глобальный шаг,\n",
        "просто передай их как аргумент к `tf.train.Checkpoint`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "hWZHyAXMqEg0",
        "colab": {}
      },
      "source": [
        "import os\n",
        "import tempfile\n",
        "\n",
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),\n",
        "  tf.keras.layers.GlobalAveragePooling2D(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])\n",
        "optimizer = tf.train.AdamOptimizer(learning_rate=0.001)\n",
        "checkpoint_dir = tempfile.mkdtemp()\n",
        "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt\")\n",
        "root = tf.train.Checkpoint(optimizer=optimizer,\n",
        "                           model=model,\n",
        "                           optimizer_step=tf.train.get_or_create_global_step())\n",
        "\n",
        "root.save(checkpoint_prefix)\n",
        "root.restore(tf.train.latest_checkpoint(checkpoint_dir))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3yoD0VJ7qEg3"
      },
      "source": [
        "### Объектно-ориентированные показатели\n",
        "\n",
        "`tfe.metrics` сохраняются как объекты. Обнови показатели модели, передав\n",
        "новые данные к вызываемому объекту, и получи результат при помощи\n",
        "метода `tf.metrics.result` как в этом примере:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "9ccu0iAaqEg5",
        "colab": {}
      },
      "source": [
        "m = tfe.metrics.Mean(\"loss\")\n",
        "m(0)\n",
        "m(5)\n",
        "m.result()  # => 2.5\n",
        "m([8, 9])\n",
        "m.result()  # => 5.5"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BE8cXErYqEg8"
      },
      "source": [
        "#### Статистика обучения в TensorBoard\n",
        "\n",
        "[TensorBoard](https://tensorflow.org/tensorboard) - это программа для\n",
        "визуализации обучения модели, которая помогает лучше понять процесс\n",
        "тренировки, отладить код и оптимизировать модель для достижения\n",
        "лучших показателей. TensorBoard записывает все ключевые моменты\n",
        "обучения во время тренировки модели.\n",
        "\n",
        "`tf.contrib.summary` совместим как с режимами eager, так и graph execution.\n",
        "Операции для записи итогов обучения, такие как `tf.contrib.summary.scalar`,\n",
        "должны быть использованы в коде во время построения модели. Вот пример\n",
        "записи показателей модели через каждые 100 глобальных шагов:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "3PXAJB1GqEg9",
        "colab": {}
      },
      "source": [
        "global_step = tf.train.get_or_create_global_step()\n",
        "\n",
        "logdir = \"./tb/\"\n",
        "writer = tf.contrib.summary.create_file_writer(logdir)\n",
        "writer.set_as_default()\n",
        "\n",
        "for _ in range(10):\n",
        "  global_step.assign_add(1)\n",
        "  # Обязательно укажи метод `record_summaries`\n",
        "  with tf.contrib.summary.record_summaries_every_n_global_steps(100):\n",
        "    # Основной код для построения модели идет ниже\n",
        "    tf.contrib.summary.scalar('global_step', global_step)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6TJSs_wG8Spg",
        "colab": {}
      },
      "source": [
        "ls tb/"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ZZRIIgFXhLzl"
      },
      "source": [
        "## Углубленные темы автоматического дифференцирования"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xEL4yJe5qEhD"
      },
      "source": [
        "### Динамические модели\n",
        "\n",
        "`tf.GradientTape` также может быть использован для динамических\n",
        "моделей. Вот пример алгоритма [поиска линии с возвратом](https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D1%81_%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%82%D0%BE%D0%BC), который выглядит как обычный\n",
        "код NumPy code, но с градиентами, и этот код - дифференцируемый,\n",
        "несмотря на сложный порядок выполнения:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "L518n5dkqEhE",
        "colab": {}
      },
      "source": [
        "def line_search_step(fn, init_x, rate=1.0):\n",
        "  with tf.GradientTape() as tape:\n",
        "    # Переменные записываются автоматически, но следим за тензором вручную.\n",
        "    tape.watch(init_x)\n",
        "    value = fn(init_x)\n",
        "  grad = tape.gradient(value, init_x)\n",
        "  grad_norm = tf.reduce_sum(grad * grad)\n",
        "  init_value = value\n",
        "  while value > init_value - rate * grad_norm:\n",
        "    x = init_x - rate * grad\n",
        "    value = fn(x)\n",
        "    rate /= 2.0\n",
        "  return x, value"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "03owpCFrqEhH"
      },
      "source": [
        "### Дополнительные функции для вычисления градиентов\n",
        "\n",
        "`tf.GradientTape` хоть и является эффективным интерфейсом для расчета\n",
        "градиентов, однако существует также другой API, который работает в стиле\n",
        "[Autograd](https://github.com/HIPS/autograd) и используется для автоматического\n",
        "дифференцирования. Эти функции весьма полезны при написании математического\n",
        "кода только с тензорами и функциями градиентов, но без `tf.variables`:\n",
        "\n",
        "* `tfe.gradients_function` — Возвращает функцию, которая рассчитывает\n",
        "производные ее параметров функции ввода с учетом аргументов. Параметр\n",
        "функции ввода должен возвращать скалярное значение. Когда мы вызываем\n",
        "возвращенную функцию, она возвращает список объектов `tf.Tensor`: по одному\n",
        "элементу для каждого аргумента функции ввода. Поскольку нам могут быть интересны\n",
        "любые параметры функции, этот метод может быть весьма неудобным в\n",
        "использовании, если есть какие-либо зависимости от множества\n",
        "обучаемых параметров\n",
        "* `tfe.value_and_gradients_function` — Похожа на `tfe.gradients_function`,\n",
        "но когда мы вызываем возвращенную функцию, она возвращает значение функции\n",
        "ввода в дополнение к списку производных функции ввода с учетом ее аргументов\n",
        "\n",
        "В следующем примере `tf.gradients_function` принимает функцию `square`\n",
        "как аргумент и возвращает функцию, которая рассчитывает частную производную\n",
        "`square` с учетом ее вводов. Для вычисления квадрата производной `square` числа `3`,\n",
        "`grad(3.0)` возвращает `6`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "QDPFUG-68i-B",
        "colab": {}
      },
      "source": [
        "def square(x):\n",
        "  return tf.multiply(x, x)\n",
        "\n",
        "grad = tfe.gradients_function(square)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "_Ow9LHre8k_3",
        "colab": {}
      },
      "source": [
        "square(3.).numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Rg5ea0kC8nEQ",
        "colab": {}
      },
      "source": [
        "grad(3.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "41D1LzcG87p8",
        "colab": {}
      },
      "source": [
        "# Квадрат производной второго порядка\n",
        "gradgrad = tfe.gradients_function(lambda x: grad(x)[0])\n",
        "gradgrad(3.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "DotEGcx5qEhH",
        "colab": {}
      },
      "source": [
        "# Производная третьего порядка - None\n",
        "gradgradgrad = tfe.gradients_function(lambda x: gradgrad(x)[0])\n",
        "gradgradgrad(3.)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "9DRAews99F4-",
        "colab": {}
      },
      "source": [
        "# С потоком выполнения\n",
        "def abs(x):\n",
        "  return x if x > 0. else -x\n",
        "\n",
        "grad = tfe.gradients_function(abs)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hTA1jmu_9gyB",
        "colab": {}
      },
      "source": [
        "grad(3.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7zs8JpKw9kir",
        "colab": {}
      },
      "source": [
        "grad(-3.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "gieGOf_DqEhK"
      },
      "source": [
        "### Собственные градиенты\n",
        "\n",
        "Создание собственных градиентов - это простой способ переписать\n",
        "стандартные градиенты в режимах eager и graph execution. В блоке\n",
        "функции прямого прохода определи градиент с учетом вводов, выводов\n",
        "и промежуточных результатов. Например, вот легкий способ сжатия\n",
        "норм градиентов в обратном проходе `backward pass`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "-OwwsWUAqEhK",
        "colab": {}
      },
      "source": [
        "@tf.custom_gradient\n",
        "def clip_gradient_by_norm(x, norm):\n",
        "  y = tf.identity(x)\n",
        "  def grad_fn(dresult):\n",
        "    return [tf.clip_by_norm(dresult, norm), None]\n",
        "  return y, grad_fn"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JPLDHkF_qEhN"
      },
      "source": [
        "Собственные градиенты часто используются для обеспечения численно\n",
        "стабильных градиентов для последовательности операций:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "24WiLROnqEhO",
        "colab": {}
      },
      "source": [
        "def log1pexp(x):\n",
        "  return tf.log(1 + tf.exp(x))\n",
        "grad_log1pexp = tfe.gradients_function(log1pexp)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "n8fq69r9-B-c",
        "colab": {}
      },
      "source": [
        "# Вычисление градиента работает отлично если x = 0.\n",
        "grad_log1pexp(0.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "_VFSU0mG-FSp",
        "colab": {}
      },
      "source": [
        "# Однако, при x = 100 расчет не производится по причине числовой нестабильности.\n",
        "grad_log1pexp(100.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-VcTR34rqEhQ"
      },
      "source": [
        "Функцию `log1pexp` можно аналитически упростить при помощи\n",
        "собственного производного градиента. Реализация этого подхода,\n",
        "как в примере ниже, повторно использует значение `tf.exp(x)`,\n",
        "которое было рассчитано во время прямого прохода,  делает\n",
        "данный метод более эффективным за счет устранения\n",
        "избыточных вычислений:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "Q7nvfx_-qEhS",
        "colab": {}
      },
      "source": [
        "@tf.custom_gradient\n",
        "def log1pexp(x):\n",
        "  e = tf.exp(x)\n",
        "  def grad(dy):\n",
        "    return dy * (1 - 1 / (1 + e))\n",
        "  return tf.log(1 + e), grad\n",
        "\n",
        "grad_log1pexp = tfe.gradients_function(log1pexp)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5gHPKMfl-Kge",
        "colab": {}
      },
      "source": [
        "# Как и в предыдущий раз, вычисление градиента работает если x = 0.\n",
        "grad_log1pexp(0.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "u38MOfz3-MDE",
        "colab": {}
      },
      "source": [
        "# А также теперь можно рассчитать градиент и при x = 100.\n",
        "grad_log1pexp(100.)[0].numpy()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rnZXjfQzqEhV"
      },
      "source": [
        "## Улучшение производительности\n",
        "\n",
        "Вычисления автоматически распределяются на графическом процессоре GPU во время\n",
        "работы в режиме eager execution. Если ты хочешь контролировать на каком\n",
        "устройстве производить вычисления, то можно просто указать его\n",
        "в блоке `tf.device('/gpu:0')` (или другом эквиваленте центрального процессора (далее в тексте и коде - CPU)):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "Ac9Y64H-qEhX",
        "colab": {}
      },
      "source": [
        "import time\n",
        "\n",
        "def measure(x, steps):\n",
        "  # TensorFlow инициализирует GPU во время первого использования,\n",
        "  # это время исключается из учета производительности.\n",
        "  tf.matmul(x, x)\n",
        "  start = time.time()\n",
        "  for i in range(steps):\n",
        "    x = tf.matmul(x, x)\n",
        "  # `tf.matmul` может возвращать значения до завершения процесса умножения\n",
        "  # матриц (например, эта функция может возвращать значения сразу после\n",
        "  # постановки операции в очередь в поток CUDA). Вызов x.numpy() ниже\n",
        "  # удостоверяет, что все операции в очереди были завершены (также копирует\n",
        "  # результаты в память устройства, с которого производились расчеты,\n",
        "  # таким образом мы учитываем немного больше времени, чем просто для\n",
        "  # операции умножения матриц `matmul`).\n",
        "  _ = x.numpy()\n",
        "  end = time.time()\n",
        "  return end - start\n",
        "\n",
        "shape = (1000, 1000)\n",
        "steps = 200\n",
        "print(\"Время, требующееся для умножения {} матрицы на себя {} раз:\".format(shape, steps))\n",
        "\n",
        "# Запускаем на CPU\n",
        "with tf.device(\"/cpu:0\"):\n",
        "  print(\"СPU: {} секунд\".format(measure(tf.random_normal(shape), steps)))\n",
        "\n",
        "# Если доступен, запускаем на GPU\n",
        "if tfe.num_gpus() > 0:\n",
        "  with tf.device(\"/gpu:0\"):\n",
        "    print(\"GPU: {} секунд\".format(measure(tf.random_normal(shape), steps)))\n",
        "else:\n",
        "  print(\"GPU: устройство не найдено\")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "RLw3IS7UqEhe"
      },
      "source": [
        "Объект `tf.Tensor` может быть скопирован на другое устройство для\n",
        "выполнения своих операций:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "ny6LX2BVqEhf",
        "colab": {}
      },
      "source": [
        "if tf.test.is_gpu_available():\n",
        "  x = tf.random_normal([10, 10])\n",
        "\n",
        "  x_gpu0 = x.gpu()\n",
        "  x_cpu = x.cpu()\n",
        "\n",
        "  _ = tf.matmul(x_cpu, x_cpu)    # Запускаем на CPU\n",
        "  _ = tf.matmul(x_gpu0, x_gpu0)  # Запускаем на первом GPU:0\n",
        "\n",
        "  if tfe.num_gpus() > 1:\n",
        "    x_gpu1 = x.gpu(1)\n",
        "    _ = tf.matmul(x_gpu1, x_gpu1)  # Запускаем на втором GPU:1"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "oA_qaII3-p6c"
      },
      "source": [
        "### Тестирование\n",
        "\n",
        "Для вычисления больших моделей, например таких, как [ResNet50](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples/resnet50),\n",
        "обучение производится на GPU, а производительность работы в\n",
        "eager execution сопоставима с graph execution. Тем не менее этот пробел\n",
        "становится еще шире для моделей, где требуется меньше вычислений, и\n",
        "где требуется больше работы для оптимизации тех блоков кода, на которые\n",
        "приходится основная нагрузка при вычислении. Это особенно актуально\n",
        "для моделей с большим количеством малых операций."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TjMTyFIp-sSE"
      },
      "source": [
        "## Работаем с графами\n",
        "\n",
        "В то время как режим eager execution делает разработку и отладку кода\n",
        "более интерактивной, graph execution в TensorFlow имеет преимущества\n",
        "для распределенного обучения, оптимизации производительности\n",
        "и развертывании моделей в продакшене. Однако, при написании кода\n",
        "в graph может сильно отличаться от стандартного кода Python, и как\n",
        "следствие, может быть более сложным для отладки.\n",
        "\n",
        "Для построения и обучения моделей в графах, программа Python\n",
        "сначала создает граф, который будет представлять расчет, а затем\n",
        "вызывает `Session.run` для отправки графа в исполнение в рабочую\n",
        "среду на C++. Это включает в себя следующие шаги:\n",
        "\n",
        "* Автоматическое дифференцирование при помощи статичного autodiff\n",
        "* Простое развертывание  на платформе независимого сервера\n",
        "* Оптимизации на графах (стандартные устранения подвыражений, свертка констант и так далее)\n",
        "* Компиляция и слияние ядра\n",
        "* Автоматическое распределение и репликация (размещение графов в распределенной системе)\n",
        "\n",
        "Развертывание кода, написанного для eager execution - более сложная задача:\n",
        "либо генерировать граф из модели, либо запустить рабочую среду Python\n",
        "и написать код непосредственно на сервер."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "hll3ZbE5qEhh"
      },
      "source": [
        "### Пишем совместимый код\n",
        "\n",
        "Один и тот же код, написанный для работы в eager execution также построит\n",
        "граф в режиме graph execution. Это можно сделать просто запустив тот же\n",
        "код в новой сессии Python, где не активирован eager execution.\n",
        "\n",
        "Большинство операций TensorFlow работают в eager execution, но есть несколько\n",
        "моментов, о которых обязательно нужно помнить:\n",
        "\n",
        "* Используй `tf.data` для обработки ввода вместо очередей. Это быстрее и проще\n",
        "* Используй объектно-ориентированные слои API - например такие, как `tf.keras.layers`\n",
        "   и `tf.keras.Model` - так как они имеют специальное место для хранения переменных\n",
        "* Большинство кода моделей работают одинаково как в eager, так и graph execution,\n",
        "   однако все-такие есть определенные исключения: например, динамические модели во время\n",
        "   порядка выполнения Python изменяют вычисления на основе ввода\n",
        "* Как только eager execution активирован при помощи `tf.enable_eager_execution`,\n",
        "   он не может быть выключен. Начни новую сессию Python для возвращения\n",
        "   к graph execution\n",
        "\n",
        "Лучше всего писать код сразу для eager и graph execution. Это даст тебе\n",
        "возможность для интерактивных экспериментов и возможность отладки кода\n",
        "в eager, а также обеспечит лучшую распределенную производительность в режиме\n",
        "graph execution.\n",
        "\n",
        "Пиши код, отлавливай баги, повторяй любые циклы операций в режиме eager\n",
        "execution, затем импортируй граф модели для развертывания в продакшене. Используй\n",
        "`tf.train.Checkpoint` для сохранения и загрузки переменных моделей, это\n",
        "обеспечит легкое передвижение между режимами eager и graph.\n",
        "Смотри больше примеров здесь:\n",
        "[tensorflow/contrib/eager/python/examples](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "sPoSUqmL-ts5"
      },
      "source": [
        "### Использование режима eager в graph\n",
        "\n",
        "Ты можешь выборочно активировать eager execution в режиме graph в TensorFlow\n",
        "при помощи `tfe.py_func`. Эту функцию следует использовать когда `tf.enable_eager_execution()`\n",
        "*не был вызван*."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "attributes": {
          "classes": [
            "py"
          ],
          "id": ""
        },
        "colab_type": "code",
        "id": "Lks-3LB0qEhi",
        "colab": {}
      },
      "source": [
        "def my_py_func(x):\n",
        "  x = tf.matmul(x, x)  # Ты можешь выполнять операции tf\n",
        "  print(x)  # даже в режиме eager!\n",
        "  return x\n",
        "\n",
        "with tf.Session() as sess:\n",
        "  x = tf.placeholder(dtype=tf.float32)\n",
        "  # Вызовем функцию eager в режиме graph!\n",
        "  pf = tfe.py_func(my_py_func, [x], tf.float32)\n",
        "\n",
        "  sess.run(pf, feed_dict={x: [[2.0]]})  # [[4.0]]"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}